import "reflect-metadata";
import { Material } from "../materials/Material";
import { Object3D } from "./Object3D";
import { Vector3 } from "../math/Vector3";
import { MapKey, Mapper } from "../utils/Map";
import { defineReactive } from "../utils/Observe";
import { post } from "../utils/Client";
import { Vector2 } from "../math/Vector2";

/**3d场景对象 */
class Node3D extends Object3D {
  @Mapper("bhidden")
  hidden: boolean = false;
  tags: Array<string> = [];
  @Mapper("material", Material)
  materials: Material[] = [];
  @Mapper("bshadow")
  shadow: boolean = true;
  @Mapper("highlight")
  highLight: boolean = false;

  userData: any = undefined;

  constructor(options?: any) {
    super();
    Node3D.prototype.isNode3D = true;

    Mapper.map(this, options);
    this.materials.forEach((item) => {
      item.guid = this.guid;
    });

    defineReactive(this, "position", (value, key) => {
      this.putProperty(key, key);
    });
    defineReactive(this, "rotation", (value, key) => {
      this.putProperty(key, "rotator");
    });
    defineReactive(this, "scale", (value, key) => {
      this.putProperty(key, key);
    });
    defineReactive(this, "hidden", (value, key) => {
      const { property } = Reflect.getMetadata(MapKey, this, key);
      this.putProperty(key, key, false, property);
    });
    defineReactive(this, "shadow", (value, key) => {
      const { property } = Reflect.getMetadata(MapKey, this, key);
      this.putProperty(key, key, false, property);
    });
    defineReactive(this, "highLight", (value, key) => {
      const { property } = Reflect.getMetadata(MapKey, this, key);
      this.putProperty(key, key, false, property);
    });
    defineReactive(this, "tags", (value, key) => {
      post("object_control_tag", Object.assign({ tags: this.tags }, { key }));
    });
  }

  private putProperty(prop, key, expand?, alias?) {
    if (expand) {
      post("object_control_basics", {
        ...this[prop],
        guid: this.guid,
        key,
      });
    } else {
      post("object_control_basics", {
        [alias || prop]: this[prop],
        guid: this.guid,
        key,
      });
    }
  }
  /**获取对象屏幕坐标 */
  screenPositionSync(): Promise<Vector2> {
    return post("object_control_basics", {
      guid: this.guid,
      key: "screenPosition",
    }).then((res) => {
      return new Vector2(res.screenPosition?.x, res.screenPosition?.y);
    });
  }
  /**
   * 
   * @param val 位置坐标
   * @param time 时间
   * @param lookAt 相机跟随
   */
  moveto(position, time = 1, lookAt = true) {
    post("object_control_basics", {
      key: "move",
      guid: this.guid,
      position: position,
      time: time,
      lookAt: lookAt,
    });
  }

  scaleto(scale, time = 1) {
    post("object_control_basics", {
      key: "scale",
      guid: this.guid,
      scale: scale,
      time: time,
    });
  }
  rotateto(rotation, time = 1) {
    post("object_control_basics", {
      key: "rotator",
      guid: this.guid,
      rotation: rotation,
      time: time,
    });
  }

  /**根据名称获取3d对象 */
  static getByNameSync(name): Promise<Node3D> {
    return post("object_find", { displayname: name }).then((obj) => {
      //console.log(obj);
      return new Node3D(obj);
    });
  }

  /**根据guid获取3d对象 */
  static getByGuidSync(guid): Promise<Node3D> {
    return post("object_find_guid", { guid: guid }).then((obj) => {
      return new Node3D(obj);
    });
  }

  /**根据tag获取3d对象集合 */
  static getByTagSync(...tags): Promise<Array<Node3D>> {
    return post("object_find_byTag", { tags: tags }).then((result) => {
      const objs: Array<Node3D> = [];
      result.objects.forEach((obj) => {
        objs.push(new Node3D(obj));
      });
      return objs;
    });
  }
  /**获取所有3d对象 */
  static listSync(): Promise<Array<Node3D>> {
    return post("object_find_all", {}).then((list) => {
      const objs: Array<Node3D> = [];
      list.objects.forEach((obj) => {
        objs.push(new Node3D(obj));
      });
      return objs;
    });
  }

  static getScreenPositionSync(guid: string): Promise<Vector2> {
    return post("object_control_basics", {
      guid,
      key: "screenPosition",
    }).then((res) => {
      return res.screenPosition;
    });
  }
}

export { Node3D };
